package org.dk.poitalk.dao.impl;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.dk.poitalk.dao.GenericDao;
import org.dk.poitalk.model.DomainEntity;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.memcache.MemcacheService;

public class GenericDaoImpl<T extends DomainEntity, PK extends Serializable> implements GenericDao<T, PK> {

	private static final Logger logger = Logger.getLogger(GenericDaoImpl.class.getName());
	
	@PersistenceContext
	protected transient EntityManager entityManager;

	/**
	 * The datastore service.
	 */
	private DatastoreService datastoreService;

	private MemcacheService memcacheService;

	/**
	 * Returns the datastore service.
	 * 
	 * @return the datastore service
	 */
	public final DatastoreService getDatastoreService() {
		return datastoreService;
	}

	/**
	 * Sets the datastore service.
	 * 
	 * @param datastoreService
	 *            the datastore service
	 */
	public final void setDatastoreService(
			final DatastoreService datastoreService) {
		this.datastoreService = datastoreService;
	}

	/**
	 * Returns the memcache service.
	 * 
	 * @return the memcache service
	 */
	public final MemcacheService getMemcacheService() {
		return memcacheService;
	}

	/**
	 * Sets the memcache service.
	 * 
	 * @param memcacheService
	 *            the memcache service
	 */
	public final void setMemcacheService(final MemcacheService memcacheService) {
		this.memcacheService = memcacheService;
	}
	
	@Override	
	public T find(Class<T> entityClass, PK primaryKey) {
		logger.info(String.format("GenericDao %s to find with id %s", entityClass.toString(), primaryKey.toString()));
		T rEntityClass = (T) entityManager.find(entityClass, primaryKey);
		logger.info(String.format("GenericDao %s finded with id %s",entityClass, primaryKey.toString()));
		return rEntityClass;
	}

	@Override
	public void save(T entity) {
		logger.info(String.format("GenericDao %s to save", entity.toString()));
		Key key = KeyFactory.createKey(entity.getClass().getSimpleName(), entity.toString());
		entity.setKey(key);
		this.entityManager.persist(entity);
		logger.info(String.format("GenericDao %s saved with id %s", entity, entity.getKey().toString()));
	}

	@Override
	public T saveOrUpdate(T entity) {
		logger.info(String.format("GenericDao %s to saveOrUpdate", entity.toString()));
		if (entity.getKey() == null) {
			this.save(entity);
		} else {
			entityManager.persist(entity);
		}
		return entity;
	}
	
	@Override
	public int delete(T entity) {
		try {
			logger.info(String.format("GenericDao %s to delete", entity.toString()));
			if (entity != null && entity.getKey() != null) {
				T entityTmp = (T) this.entityManager.find(entity.getClass(), entity.getKey());
				if (entityTmp != null) {
					this.entityManager.remove(entityTmp);
					logger.info(String.format("GenericDao %s deleted with id %s", entity, entity.getKey().toString()));
					return 1;
				} else {
					return 0;
				}
			} else {
				return 0;
			}
		} catch (Exception ex) {
			return 0;
		}
	}
	
	@Override
	public List<T> search(String namedQuery, Map<String, Object> mapValue) {
		logger.info(String.format("GenericDao %s to search by namedQuery", namedQuery));
		Query query = entityManager.createNamedQuery(namedQuery);
		Set<String> keys = mapValue.keySet();
		for (String key : keys) {
			Object value = mapValue.get(key);
			query.setParameter(key, value);
		}
		logger.info(String.format("GenericDao %s searched by namedQuery", namedQuery));
		return query.getResultList();
	}
	
	@Override
	public List<T> search(Map<String, String> mapValue) {
		throw new UnsupportedOperationException("No se permite la busqueda 'search(Map<String, String> mapValue)'");
	}
	
}
